package vs1;

import Bots.Bots;
import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
import cz.cuni.amis.pogamut.base.communication.worldview.listener.annotation.EventListener;
import cz.cuni.amis.pogamut.base.communication.worldview.listener.annotation.ObjectClassEventListener;
import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
import cz.cuni.amis.pogamut.base3d.worldview.object.event.WorldObjectAppearedEvent;
import cz.cuni.amis.pogamut.ut2004.agent.module.utils.TabooSet;
import cz.cuni.amis.pogamut.ut2004.agent.navigation.UT2004Navigation;
import cz.cuni.amis.pogamut.ut2004.agent.navigation.UT2004PathAutoFixer;
import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004BotModuleController;
import cz.cuni.amis.pogamut.ut2004.communication.messages.ItemType;
import cz.cuni.amis.pogamut.ut2004.communication.messages.UT2004ItemType;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.*;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.*;
import cz.cuni.amis.pogamut.ut2004.utils.UT2004BotRunner;
import cz.cuni.amis.pogamut.ut2004.utils.UnrealUtils;
import cz.cuni.amis.utils.collections.MyCollections;
import cz.cuni.amis.utils.exception.PogamutException;

import javax.vecmath.Vector3d;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

@AgentScoped
public class HunterBot extends UT2004BotModuleController<UT2004Bot> {
    //主程序1
    public static int agent_num = 2;
    public static int skill = 4;

    public static Bots[] expertbots;

    ////////////////////////////////////////////
    //逻辑
    @Override
    public void logic() { //核心每个时间步做的事（分线程）
        perf(expertbots[agt]);
    }
    ////////////////////////////////////////////////////////////////
    /////////////////////// 初始化   ///////////////////////////////////
    ////////////////////////////////////////////////////////////////

    public static boolean rw1 = false;
    public static boolean rw2 = false;
    protected int seed = 50;                      // 种子

    HunterBot() {
        super();
        this.random = new Random(seed);
        expertbots = new Bots[agent_num];
        for (int i = 0; i < agent_num; i++) {
            expertbots[i] = new Bots(i, numState, numAction, rw2, "expertbots");
        }
    }


    //public static TDFALCON_BOT falconAI;
    public static void main(String[] args) throws PogamutException, Exception {//主程序
        new UT2004BotRunner(HunterBot.class, "Hunter").setConsoleLogging(false).setMain(true).startAgents(agent_num);
    }

    Self self = null;
    //主线程准备2
    public TabooSet<Item> tabooItems;
    public UT2004PathAutoFixer autoFixer;

    public int agt;
    static AtomicInteger count = new AtomicInteger(0);

    @Override
    public void prepareBot(UT2004Bot bot) {
        agt = count.get();
        count.incrementAndGet();
        autoFixer = new UT2004PathAutoFixer(bot, getNavigation().getPathExecutor(), fwMap, aStar, navBuilder);
        tabooItems = new TabooSet<>(bot);

        getWorldView().addEventListener(BotDamaged.class, botDamagedListener);
        getWorldView().addEventListener(BotKilled.class, BotKilledListener);
        getWorldView().addEventListener(PlayerDamaged.class, PlayerDamagedListener);
        getWorldView().addEventListener(PlayerKilled.class, PlayerKilledListener);
        getWorldView().addEventListener(ItemPickedUp.class, ItemPickedUpListener);
        getWorldView().addEventListener(HearNoise.class, HearNoiseListener);

        // listeners        
        getNavigation().getState().addListener(changedValue -> {
            switch (changedValue) {
                case PATH_COMPUTATION_FAILED:
                case STUCK:
                case TARGET_REACHED:
                    getNavigation().stopNavigation();
                    break;
            }
        });


        weaponPrefs.addGeneralPref(UT2004ItemType.LIGHTNING_GUN, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.SHOCK_RIFLE, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.MINIGUN, false);
        weaponPrefs.addGeneralPref(UT2004ItemType.FLAK_CANNON, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.ROCKET_LAUNCHER, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.LINK_GUN, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.ASSAULT_RIFLE, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.BIO_RIFLE, true);


    }

    //初始化3
    //重置
    protected void reset(Bots bots) {
        bots.reset();
        if (bots.numState >= 0) System.arraycopy(bots.stat, 0, bots.CurrentStat, 0, bots.numState);
    }

    //public static int  instanceCount =0;
    @Override
    public Initialize getInitializeCommand() {   //程序开始分家
        return new Initialize().setName("EXPERT").setTeam(0).setDesiredSkill(skill);
    }

    protected UT2004Navigation navigationAStar;

    //4

    public static final String FRONT = "frontRay";
    public static final String LEFT45 = "left45Ray";
    public static final String LEFT90 = "left90Ray";
    public static final String RIGHT45 = "right45Ray";
    public static final String RIGHT90 = "right90Ray";

    public static final String BEHIND = "behindRay";
    public static final String BLEFT45 = "Bleft45Ray";
    public static final String BRIGHT45 = "Bright45Ray";

    @Override
    public void botInitialized(GameInfo info, ConfigChange currentConfig, InitedMessage init) {
        final int rayLength = (int) (UnrealUtils.CHARACTER_COLLISION_RADIUS * 2);
        boolean fastTrace = true;
        boolean floorCorrection = false;
        boolean traceActor = false;
        getAct().act(new RemoveRay("All"));
        raycasting.createRay(LEFT45, new Vector3d(1, -1, 0), rayLength, fastTrace, floorCorrection, traceActor);
        raycasting.createRay(FRONT, new Vector3d(1, 0, 0), rayLength, fastTrace, floorCorrection, traceActor);
        raycasting.createRay(RIGHT45, new Vector3d(1, 1, 0), rayLength, fastTrace, floorCorrection, traceActor);
        raycasting.createRay(LEFT90, new Vector3d(0, -1, 0), rayLength, fastTrace, floorCorrection, traceActor);
        raycasting.createRay(RIGHT90, new Vector3d(0, 1, 0), rayLength, fastTrace, floorCorrection, traceActor);

        raycasting.createRay(BRIGHT45, new Vector3d(1, -1, 0), -rayLength, fastTrace, floorCorrection, traceActor);
        raycasting.createRay(BEHIND, new Vector3d(1, 0, 0), -rayLength, fastTrace, floorCorrection, traceActor);
        raycasting.createRay(BLEFT45, new Vector3d(1, 1, 0), -rayLength, fastTrace, floorCorrection, traceActor);

        raycasting.getAllRaysInitialized().addListener(changedValue -> {
        });

        raycasting.endRayInitSequence();
        getAct().act(new Configuration().setDrawTraceLines(true).setAutoTrace(true));

    }


    //产生5
    @Override
    public void botFirstSpawn(GameInfo gameInfo, ConfigChange config, InitedMessage init, Self self) {
    }


    /////////////////////////////////////////////////////////////////
    /////////////////////// 一堆环境监控   /////////////////////////
    ////////////////////////////////////////////////////////////////
    //被打
    public IWorldEventListener<BotDamaged> botDamagedListener = new IWorldEventListener<BotDamaged>() {
        @Override
        public void notify(BotDamaged event) {
            expertbots[agt].getdamage += event.getDamage();
        }
    };


    //打人
    public IWorldEventListener<PlayerDamaged> PlayerDamagedListener = new IWorldEventListener<PlayerDamaged>() {
        @Override
        public void notify(PlayerDamaged event) {
            expertbots[agt].hitdamage += event.getDamage();

        }
    };
    boolean dead_help;
    //被杀
    public IWorldEventListener<BotKilled> BotKilledListener = new IWorldEventListener<BotKilled>() {
        @Override
        public void notify(BotKilled event) {
            getNavigation().stopNavigation();
            getAct().act(new StopShooting());
            if (!dead_help) {

                expertbots[agt].r -= 1;
                expertbots[agt].end = true;
                expertbots[agt].die++;
                expertbots[agt].enemy = null;
                expertbots[agt].reset();
                if (expertbots[agt].kill == 25 || expertbots[agt].die == 25) {
                    expertbots[agt].kill = 0;
                    expertbots[agt].die = 0;
                }
            } else {
                dead_help = false;
            }

        }
    };

    boolean kill_help;
    //杀敌
    public IWorldEventListener<PlayerKilled> PlayerKilledListener = new IWorldEventListener<PlayerKilled>() {
        @Override
        public void notify(PlayerKilled event) {
            if (event.getKiller() == info.getId()) {
                expertbots[agt].r += 1.0;
                expertbots[agt].kill += 1;
                expertbots[agt].end = true;
                expertbots[agt].enemy = null;
                if (expertbots[agt].kill == 25 || expertbots[agt].die == 25) {
                    expertbots[agt].kill = 0;
                    expertbots[agt].die = 0;
                }
                //getAct().act(new Respawn());
                //dead_help=true;
            }


        }
    };
    //拾取物品
    public IWorldEventListener<ItemPickedUp> ItemPickedUpListener = event -> {

    };
    //监听噪音
    public IWorldEventListener<HearNoise> HearNoiseListener = event -> {

    };


    //碰面
    @ObjectClassEventListener(eventClass = WorldObjectAppearedEvent.class, objectClass = Player.class)
    public void playerAppeared(WorldObjectAppearedEvent<Player> event) {

    }

    @EventListener(eventClass = TeamChat.class)
    public void teamChat(TeamChat event) {

    }


    ////////////////////////////////////////////////////////////////
    /////////////////////// 状态   ////////////////////////////////
    ///////////////////////////////////////////////////////////////

    static int numState = (12) * 2;

    //状态采样
    public void observe(Bots bots) {
        int i = 0;
        Player enemy;
        bots.Re();

        //血量
        bots.stat[2 * i] = (double) info.getHealth() / 100.0;
        if (bots.stat[2 * i] > 1) bots.stat[2 * i] = 1.0;
        bots.stat[2 * i + 1] = 1 - bots.stat[2 * i];
        i += 1;

        enemy = players.getNearestVisiblePlayer(players.getVisibleEnemies().values());
        if (enemy != null) {
            bots.stat[2 * i] = info.getLocation().getDistance(enemy.getLocation()) / 1000.0;
            if (bots.stat[2 * i] > 1) bots.stat[2 * i] = 1.0;
            bots.stat[2 * i + 1] = 1 - bots.stat[2 * i];
        }

        i += 1;
        //有敌人
        if (enemy != null) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }

        i += 1;

        //射击
        if (bots.shooting) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }

        i += 1;

        //是否被攻击
        if (senses.isBeingDamaged()) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }

        i += 1;

        //是否有激光
        if (weaponry.hasWeapon(UT2004ItemType.LIGHTNING_GUN)) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }

        i += 1;

        //是否有minigun
        if (weaponry.hasWeapon(UT2004ItemType.MINIGUN)) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }

        i += 1;

        //是否有闪光炮
        if (weaponry.hasWeapon(UT2004ItemType.FLAK_CANNON)) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }

        i += 1;

        //是否有火箭筒
        if (weaponry.hasWeapon(UT2004ItemType.ROCKET_LAUNCHER)) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }

        i += 1;

        //是否有连接枪
        if (weaponry.hasWeapon(UT2004ItemType.LINK_GUN)) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }

        i += 1;

        //是否有突击步枪
        if (weaponry.hasWeapon(UT2004ItemType.ASSAULT_RIFLE)) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }

        i += 1;

        //是否有生物枪
        if (weaponry.hasWeapon(UT2004ItemType.BIO_RIFLE)) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }

        //i+=1;
    }

    ////////////////////////////////////////////////////////////////
    /////////////////////// 动作   ////////////////////////////////
    ////////////////////////////////////////////////////////////////
    //动作（以下模块构造均相同）
    static int numAction = 5;

    public void stopshooting(Bots bots) {
        if (!players.canSeeEnemies() && (info.isShooting() || info.isSecondaryShooting())) {
            getAct().act(new StopShooting());
            bots.shooting = false;
        }
    }

    public void getRandomNavPoint(Bots bots) {
        bot.getBotName().setInfo("getRandomNavPoint");
        getNavigation().navigate(getNavPoints().getRandomNavPoint());
        if (!getNavigation().isNavigating()) {
            getNavigation().navigate(getNavPoints().getRandomNavPoint());
            return;
        }
    }

    public void RunAroundItems(Bots bots) {
        bot.getBotName().setInfo("RunAroundItems");         // 给智能体一个名字，方便观察

        if (getNavigation().isNavigatingToItem()) return;
        try {
            bots.interesting = new ArrayList<>();
            for (ItemType itemType : ItemType.Category.WEAPON.getTypes()) { // 所有武器目录
                if (!weaponry.hasWeapon(itemType) || weaponry.hasLowAmmoForWeapon(itemType, 1.0)) //如果没有这个武器，或者武器弹药缺少
                    bots.interesting.addAll(items.getSpawnedItems(itemType).values()); // 则可以去获得这个武器
            }
            for (ItemType itemType : ItemType.Category.ARMOR.getTypes()) { // 添加所有装甲
                bots.interesting.addAll(items.getSpawnedItems(itemType).values()); //
            }
            bots.interesting.addAll(items.getSpawnedItems(ItemType.Category.HEALTH).values()); //添加所有医疗物品
            Item item = null;
            if (bots.interesting.size() != 0) {
                item = MyCollections.getRandom(tabooItems.filter(bots.interesting)); //随机选取一个物品
                if (item != null) getNavigation().navigate(item);
                ItemType cate_item = item.getType();

                while (!getNavigation().isNavigating()) {
                    bots.interesting.remove(item);
                    if (bots.interesting.size() == 0)
                        getNavigation().navigate(getNavPoints().getRandomNavPoint());
                    else {
                        item = MyCollections.getRandom(tabooItems.filter(bots.interesting));
                        if (item != null) getNavigation().navigate(item);                    // 导航至目标
                    }
                }
            } else {
                getNavigation().navigate(getNavPoints().getRandomNavPoint());
            }
        } catch (Exception e) {
            getNavigation().navigate(getNavPoints().getRandomNavPoint());
        }
        bots.item = null;
    }

    //医疗2
    public void MedKit(Bots bots) {
        bot.getBotName().setInfo("MED-KIT");                           // 给智能体一个名字，方便观察
        Item item = null;

        if (bots.item != null &&
                getNavigation().isNavigatingToItem() &&
                ItemType.Category.HEALTH.getTypes().contains(bots.item.getType())) return;

        try {
            item = items.getPathNearestSpawnedItem(ItemType.Category.HEALTH); // 否则获得最近的医疗物品
            if (item != null) getNavigation().navigate(item);                        // 如果有目标则导航至目标
            bots.interesting = new ArrayList<>();        // 则任意一个医疗目标都行
            while (!getNavigation().isNavigating()) {             // 如果无法成功导航
                bots.interesting.remove(item);
                if (bots.interesting.size() == 0) {
                    getNavigation().navigate(getNavPoints().getRandomNavPoint());
                } else {
                    item = MyCollections.getRandom(tabooItems.filter(bots.interesting));
                    if (item != null) getNavigation().navigate(item);
                }

            }
        } catch (Exception e) {
            getNavigation().navigate(getNavPoints().getRandomNavPoint());
        }
        bots.item = item;                    // 将导航物品设为本物品
    }

    //射击3
    public void Engage(Bots bots) {
        bot.getBotName().setInfo("stateEngage");                 // 给智能体一个名字，方便观察

        double distance;                      // 设定一个最大距离


        if (bots.enemy == null || !bots.enemy.isVisible()) {     // 如果敌人不存在或敌人看不见
            bots.enemy = players.getNearestVisiblePlayer(players.getVisibleEnemies().values());  //设定一个敌人
            if (bots.enemy != null) if (bots.enemy.getName().toCharArray()[0] == this.info.getName().toCharArray()[0]) {
                bots.enemy = null;
            }
            if (bots.enemy == null) {                            // 假设敌人不存在
                if (info.isShooting() || info.isSecondaryShooting()) {
                    getAct().act(new StopShooting());            // 就不要射击了
                }
                getNavigation().navigate(getNavPoints().getRandomNavPoint());   // 随机移动一个位置
                return;
            }
        }    // 有了敌人后

        bots.item = null;                                        // 如果看到敌人就不导航至物品了
        if (!bots.enemy.isVisible()) {                             // 如果敌人看不到
            if (info.isShooting() || info.isSecondaryShooting()) {
                getAct().act(new StopShooting());                // 就不要射击了
                bots.shooting = false;
            }
            bots.runningToPlayer = false;                        // 没用
        } else {                                                 // 如果能看到敌人
            if (shoot.shoot(weaponPrefs, bots.enemy) != null) {     // 射击敌人
                bots.shooting = true;                            // 没用
            }
        }
        distance = info.getLocation().getDistance(bots.enemy.getLocation());   // 计算与敌人的距离
        int decentDistance = Math.round(random.nextFloat() * 800) + 200;            // 一个随机距离
        if (!bots.enemy.isVisible() || !info.isShooting() || decentDistance < distance) { // 如果敌人看不到 或者 没有在射击 或者 随机距离 小于和敌人距离
            getNavigation().navigate(bots.enemy);         // 朝向对方
            if (!getNavigation().isNavigating())
                getNavigation().navigate(getNavPoints().getRandomNavPoint());
        } else {                                         // 如果 看到敌人 且 在射击 且 随机距离大于与敌人距离
            getNavigation().stopNavigation();                 // 则原地射击
        }
    }

    public void Rotate(Bots bots) {
        bot.getBotName().setInfo("Rotate");
        if (getNavigation().isNavigating()) {
            getNavigation().stopNavigation();
            bots.item = null;
        }
        getAct().act(new Rotate().setAmount(32000));
    }

    public void Pursue(Bots bots) {
        bot.getBotName().setInfo("Pursue");
        if (bots.enemy == null || !getNavigation().isNavigatingToPlayer()) {
            if (bots.enemy == null) {
                bots.enemy = players.getNearestVisiblePlayer(players.getVisibleEnemies().values());
                if (bots.enemy != null)
                    if (bots.enemy.getName().toCharArray()[0] == this.info.getName().toCharArray()[0]) {
                        bots.enemy = null;
                    }
            }
            if (bots.enemy == null) {
                RunAroundItems(bots);
            } else {
                getNavigation().navigate(bots.enemy);
                bots.item = null;
                if (!getNavigation().isNavigating()) {
                    bots.enemy = null;
                    RunAroundItems(bots);
                }

            }
        }
    }

    /////////////////////////////////////////////////////////////////
    /////////////////////// 运行部分   //////////////////////////////
    ////////////////////////////////////////////////////////////////

    public int healthLevel = 75;

    //ai部分
    public void perf(Bots bots) {
        if (!weaponry.hasAmmoForWeapon(UT2004ItemType.ASSAULT_RIFLE)) {
            bots.interesting = new ArrayList<>();
            bots.interesting.addAll(items.getSpawnedItems(UT2004ItemType.ASSAULT_RIFLE).values());
            Item item = MyCollections.getRandom(tabooItems.filter(bots.interesting));
            if (item == null) {
                getNavigation().navigate(getNavPoints().getRandomNavPoint());
            } else {
                if (item != null) getNavigation().navigate(item);
                if (!getNavigation().isNavigating()) {
                    getNavigation().navigate(getNavPoints().getRandomNavPoint());
                } else {
                    bots.item = item;
                }
            }
        }
        // 1) do you see enemy? 	-> go to PURSUE (start shooting / hunt the enemy)
        if (bots.enemy == null || !bots.enemy.isVisible())
            bots.enemy = players.getNearestVisiblePlayer(players.getVisibleEnemies().values());
        if (bots.enemy != null) if (bots.enemy.getName().toCharArray()[0] == this.info.getName().toCharArray()[0]) {
            bots.enemy = null;
        }
        if (players.canSeeEnemies() && weaponry.hasLoadedWeapon() && bots.enemy != null) {
            //System.out.println("Engage");
            Engage(bots);
            return;
        }

        // 2) are you shooting? 	-> stop shooting, you've lost your target
        if (info.isShooting() || info.isSecondaryShooting()) {
            getAct().act(new StopShooting());
        }

        // 3) are you being shot? 	-> go to HIT (turn around - try to find your enemy)
        if (senses.isBeingDamaged()) {
            //System.out.println("Rotate");
            Rotate(bots);
            return;
        }

        // 4) have you got enemy to pursue? -> go to the last position of enemy
        if (bots.enemy != null && weaponry.hasLoadedWeapon()) {  // !enemy.isVisible() because of 2)
            //System.out.println("Pursue");
            Pursue(bots);
            return;
        }

        // 5) are you hurt?			-> get yourself some medKit
        if (info.getHealth() < healthLevel) {
            //System.out.println("MedKit");
            MedKit(bots);
            return;
        }

        // 6) if nothing ... run around items
        //System.out.println("RunAroundItems");
        RunAroundItems(bots);
    }


}